#ifndef QPARSETREERENDER_H
#define QPARSETREERENDER_H

#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QGraphicsLineItem>
#include <QLinkedList>
#include <QLinearGradient>
#include <QGraphicsSceneMouseEvent>
#include <QStack>
#include "../fractallib/TimeSeries.h"
#include "../fractallib/parsers/SinglePass.h"
#include "../fractallib/forecasting/Forecast.h"
#include "Utils.h"
#include <cmath>

Q_DECLARE_METATYPE(FL::Trees::Node)

class QParseTreeScene: public QGraphicsScene
{
    Q_OBJECT
public:
    QParseTreeScene(): QGraphicsScene(0)
    {

    }

    ~QParseTreeScene()
    {

    }

protected:
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    {
        emit onMouseEvent(event);
        event->accept();
    }

    virtual void wheelEvent(QGraphicsSceneWheelEvent *event)
    {
        emit onWheelEvent(event);
        event->accept();
    }

signals:
    void onMouseEvent(QGraphicsSceneMouseEvent *event);
    void onWheelEvent(QGraphicsSceneWheelEvent *event);
};

enum LayerDrawingOptions
{
    ldoNone             = 0x0,
    ldoDrawTimeSeries   = 0x1  //!< Draw time series generated by layer
};

enum ForecastStyle {
    fsNone               = 0x0,
    fsAllForest           = 0x1,
    fsIntersectionOnly   = 0x2
};

class QParseTreeRender
{
public:


public:
    QParseTreeRender();
    virtual ~QParseTreeRender();

    void setTimeSeries(FL::TimeSeries *ts);
    void timeSeriesChanged();

    void setForest(FL::Trees::Forest *forest);
    void forestChanged();

    void addForecast(const FL::Forecasting::Forecast &forecast);
    void clearForecasts() { m_forecasts.clear(); }
    int forecastCount() { return m_forecasts.size(); }
    FL::Forecasting::Forecast getForecast(int i) { return m_forecasts[i]; }
    void setMaxForecastCount(int value) { m_maxForecastCount = value; }
    void forecastChanged();

    void setView(QGraphicsView *view);

    QParseTreeScene* scene() { return m_scene; }

    bool showRoots() const { return m_showRoots; }
    void setShowRoots(bool value) { m_showRoots = value; }

    int currentTree() const { return m_currentTree; }
    void setCurrentTree(int value) {
        m_currentTree = value;
        if (m_currentTree < 0)
            m_currentTree = 0;
        if (m_currentTree >= (int)m_forest->size())
            m_currentTree = m_forest->size() - 1;
        forestChanged();
    }

    bool isShowAllForecasts() const
    {
        return m_isShowAllForecasts;
    }
    void setIsShowAllForecasts(bool value)
    {
        m_isShowAllForecasts = value;
        timeSeriesChanged();
    }

    void fitScene();

    double scale() const { return m_scale; }
    void setScale(double value) {
        m_scale = value;
        forestChanged();
    }


    void setMousePos(double x, double y);
    double currentValue(bool *ok) const;
    double currentTime(bool *ok) const;

    const FL::Trees::Node* currentNode() const;

    ForecastStyle forecastStyle() const { return m_forecastStyle; }
    void setForecastStyle(ForecastStyle forecastStyle) {
        m_forecastStyle = forecastStyle;
        forecastChanged();
    }

    void setYLogScale(bool value);
    bool isYLogScale() const { return m_isYLogScale; }

    void setFontSize(int size) { m_fontSize = std::max(size, 1); draw(); }
    int fontSize() { return m_fontSize; }
protected:
    void draw();
    void drawCoordinateSystem();
    void drawTimeSeries();
    void drawForecast();
    void drawParseTree(FL::Trees::Tree *tree);
    void drawForest();
    void drawTreeLayer(const FL::Trees::Layer &layer,
                       QColor color,
                       LayerDrawingOptions options = ldoNone);
    void prepare();

    inline double tox(const double &x) const
    {
        //return ((x - m_tsMinTime) / (m_tsMaxTime - m_tsMinTime));
        return x;
    }

    inline double toy(const double &y) const
    {
        if (!m_isYLogScale)
            return y;
        return log(y);
        //return (y / m_yHalfRange);
    }


private:
    int m_currentTree;
    bool m_showRoots;
    bool m_isYLogScale;

    int m_fontSize;

    static const int ASSIGNED_NODE = 1;
private:
    QParseTreeScene *m_scene;
    QGraphicsView *m_view;
    FL::TimeSeries *m_ts;
    FL::Trees::Forest *m_forest;
    QVector<FL::Forecasting::Forecast> m_forecasts;

    double m_tsMinValue;
    double m_tsMaxValue;
    double m_tsMinTime;
    double m_tsMaxTime;
    double m_scale;
    bool m_isShowAllForecasts;

    double m_mouseX, m_mouseY;

    FL::Trees::Node m_currentNode;
    bool m_haveCurrentNode;

    double m_yHalfRange;
    ForecastStyle m_forecastStyle;

    int m_maxForecastCount;
    //QLinkedList<QGraphicsItem*> m_tsItems;
    //QLinkedList<QGraphicsItem*> m_coordSystemItems;
};

#endif // QPARSETREERENDER_H
